package com.antcom.antread.common.core.baseRest.dao;

import com.antcom.antread.common.core.baseRest.domain.BaseDomain;
import com.antcom.antread.common.core.baseRest.domain.FieldType;
import com.antcom.antread.common.core.baseRest.domain.PageDevice;
import com.antcom.antread.common.core.baseRest.domain.QueryConditions;
import com.antcom.antread.common.core.constant.PageConstants;
import com.antcom.antread.common.core.utils.date.DateUtils;
import com.antcom.antread.common.core.utils.string.StringUtils;
import com.google.common.collect.Lists;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * dao: 基类
 *
 * @Author 中国蚂蚁技术有限公司
 * @create 2020/12/27 13:25
 */
public class BaseRestDao<R extends JpaRepository, S extends JpaSpecificationExecutor, E extends BaseDomain> extends EntityManagerDao{

    /**
     * jpaRepository
     */
    @Inject
    protected R repository;

    /**
     * jpaSpecificationExecutor
     */
    @Inject
    protected S specificationExecutor;

    /**
     * 分页查询
     *
     * @param entity 实体类
     * @param page   分页
     * @return
     */
    public PageDevice getPage(E entity, PageDevice page) {
        // 单独查询每一条详情
        Specification specification = new Specification() {

            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {

                List<Predicate> list = Lists.newArrayList();

                if (page.getQueryConditions() != null && page.getQueryConditions().size() > 0) {
                    for (QueryConditions item : page.getQueryConditions()) {

                        // 查询条件
                        if (StringUtils.isEmpty(item.getField())) {
                            continue;
                        }
                        Predicate predicate = null;
                        try {
                            predicate = queryConditionsConvert(root, criteriaBuilder, item, entity);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (predicate!=null){
                            list.add(predicate);
                        }

                    }
                }
                return criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
            }
        };

        //获取排序的属性以及升序还是降序
        Sort sort = querySortCover(page.getSortMap());
        if (page.getPageNum() < 1) {
            page.setPageNum(1);
        }

        PageRequest pageRequest = PageRequest.of(page.getPageNum() - 1, page.getPageTotal(), sort);
        Page pageDevice = specificationExecutor.findAll(specification, pageRequest);
        List<E> list = pageDevice.getContent();
        page.setList(list);
        page.setTotalCount(String.valueOf(pageDevice.getTotalElements()));
        return page;
    }

    /**
     * 查询条件拼接
     *
     * @param root
     * @param criteriaBuilder
     * @param queryConditions
     * @param entity
     * @return
     * @throws Exception
     */
    public Predicate queryConditionsConvert(Root root, CriteriaBuilder criteriaBuilder, QueryConditions queryConditions, E entity) throws Exception {
        Field field = entity.getClass().getDeclaredField(queryConditions.getField());
        // 判断查询条件是否完备
        if (queryConditions == null || !queryConditions.isAvailable() || field == null) {
            return null;
        }

        Predicate predicate = null;
        // 字段名称
        String fieldName = queryConditions.getField();
        // 最小值
        Object valueMin = queryConditions.getValueMin();
        // 最大值
        Object valueMax = queryConditions.getValueMax();

        // 设置时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

        // 若是作者则直接匹配书籍
        if (FieldType.AUTHOR.equals(queryConditions.getType())) {
            if (queryConditions.getThisLevel()) {
                predicate = criteriaBuilder.equal(root.get(fieldName), valueMin.toString());
            } else {
                predicate = criteriaBuilder.like(root.get(fieldName), "%" + valueMin.toString());
            }
            return predicate;
        }

        switch (queryConditions.getComparison()){
            case EQUALS:
                predicate=criteriaBuilder.equal(root.get(fieldName), valueMin.toString());
                break;
            case BLURRY:
                predicate=criteriaBuilder.like(root.get(fieldName), "%" + valueMin.toString() + "%");
                break;
            case LEFTBLURRY:
                predicate=criteriaBuilder.like(root.get(fieldName), "%" + valueMin.toString());
                break;
            case RIGHTBLURRY:
                predicate=criteriaBuilder.like(root.get(fieldName), valueMin.toString()+ "%");
                break;
            case BETWEEN:
                if(FieldType.NUMBER.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName),  Double.valueOf(valueMin.toString()), Double.valueOf(valueMax.toString()));
                }else if(FieldType.DATE.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName), simpleDateFormat.parse(valueMin.toString()),simpleDateFormat.parse(valueMax.toString()) );
                }
                break;
            case LESS:
                if(FieldType.NUMBER.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.lessThan(root.get(fieldName), Double.valueOf(valueMin.toString()));
                }else if(FieldType.DATE.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName), simpleDateFormat.parse(valueMin.toString())
                            , DateUtils.dateAddYears(simpleDateFormat.parse(valueMin.toString()), PageConstants.EXPAND_YEAR));
                }
                break;
            case LESSANDEQUALS:
                if(FieldType.NUMBER.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.le(root.get(fieldName), Double.valueOf(valueMin.toString()));
                }else if(FieldType.DATE.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName), simpleDateFormat.parse(valueMin.toString())
                            , DateUtils.dateAddYears(simpleDateFormat.parse(valueMin.toString()), PageConstants.EXPAND_YEAR));
                }
                break;
            case  GREATER:
                if(FieldType.NUMBER.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.greaterThan(root.get(fieldName), Double.valueOf(valueMin.toString()));
                }else if(FieldType.DATE.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName), simpleDateFormat.parse(valueMin.toString())
                            , DateUtils.dateAddYears(simpleDateFormat.parse(valueMin.toString()), PageConstants.EXPAND_YEAR));
                }
                break;
            case GREATERANDEQUALS:
                if(FieldType.NUMBER.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.ge(root.get(fieldName), Double.valueOf(valueMin.toString()));
                }else if(FieldType.DATE.equals(queryConditions.getType())){
                    predicate=criteriaBuilder.between(
                            root.get(fieldName), simpleDateFormat.parse(valueMin.toString())
                            , DateUtils.dateAddYears(simpleDateFormat.parse(valueMin.toString()), PageConstants.EXPAND_YEAR));
                }
                break;
            default:
                predicate=null;
                break;
        }
        return predicate;
    }

    /**
     * 获取属性的排序信息，封装为Sort.Order类型
     *
     * @param sortMap 需要排序的属性
     * @return 封装的Sort对象，提供给JpaSpecificationExecutor分页排序使用
     */
    private Sort querySortCover(Map<String, String> sortMap) {
        List<Sort.Order> orders = new ArrayList<Sort.Order>();
        //orders不能为空，所以如果为空设置按id排序
        if (sortMap == null || sortMap.size() == 0) {
            orders.add(new Sort.Order(Sort.Direction.ASC, "id"));
        } else {
            for (Map.Entry<String, String> entry : sortMap.entrySet()) {
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();
                orders.add(new Sort.Order(Sort.Direction.fromString(mapValue), mapKey));
            }
        }
        return Sort.by(orders);
    }

    /**
     * 获取单条数据
     *
     * @param id 数据的标识
     * @return 要获取的数据信息
     */
    public E getOne(String id) {
        Optional optional = repository.findById(id);
        if (optional.isPresent()) {
            return (E) optional.get();
        }
        return null;
    }

    /**
     * 获取集合
     *
     * @return
     */
    public List<E> getList() {
        return repository.findAll();
    }

    /**
     * 保存
     *
     * @param entity 单个实体
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(E entity) {
        repository.save(entity);
    }

    /**
     * 保存多个
     *
     * @param list 实体类集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveList(List<E> list) {
        repository.saveAll(list);
    }

    /**
     * 删除根据id
     *
     * @param id 主键
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(String id) {
        repository.deleteById(id);
    }

    /**
     * 删除根据实体
     *
     * @param entity
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(E entity) {
        repository.delete(entity);
    }

    public Boolean exists(String id) {
        return repository.existsById(id);
    }
}
